﻿using System.Linq.Expressions;
using Devonline.AspNetCore;
using Devonline.Database.MongoDB.Models;
using Devonline.Database.NoSQL;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Linq;

namespace Devonline.Database.MongoDB;

/// <summary>
/// MongoDB 数据服务得默认实现
/// </summary>
public class MongoDataService : NoSQLDataService, IMongoDataService, INoSQLDataService
{
    private readonly IMongoDatabase _database;

    public MongoDataService(
        ILogger<MongoDataService> logger,
        IDatabaseEndpoint endpoint,
        IHttpContextAccessor httpContextAccessor
        ) : base(logger, endpoint, httpContextAccessor) => _database = new MongoClient(endpoint.ConnectionString).GetDatabase(endpoint.Database);

    /// <summary>
    /// 根据过滤条件返回查询结果list
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <returns></returns>
    public override async Task<IQueryable<TModel>> GetQueryableAsync<TModel>()
    {
        _logger.LogDebug("用户 {userName} 将查询 {host} 的数据库 {database}", _httpContext.GetUserName(), _endpoint.Host, _endpoint.Database);
        var collection = await GetOrCreateCollectionAsync<TModel>();
        return collection.AsQueryable();
    }
    /// <summary>
    /// 根据过滤条件返回查询结果list
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="predicate">查询表达式</param>
    /// <returns></returns>
    public override async Task<IQueryable<TModel>> GetQueryableAsync<TModel>(Expression<Func<TModel, bool>> predicate) => (await GetQueryableAsync<TModel>()).Where(predicate);
    /// <summary>
    /// 根据条件查询第一条记录
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="predicate">查询表达式</param>
    /// <returns></returns>
    public override async Task<TModel?> FirstOrDefaultAsync<TModel>(Expression<Func<TModel, bool>> predicate) where TModel : class => (await GetQueryableAsync<TModel>(predicate)).FirstOrDefault();
    /// <summary>
    /// 从数据库查询多行数据
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <returns></returns>
    public override async Task<PagedResult<TModel>> GetPagedResultAsync<TModel>() => await _request.GetPagedResultAsync<TModel>(await GetQueryableAsync<TModel>());
    /// <summary>
    /// 从数据库查询多行数据
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="predicate">查询表达式</param>
    /// <returns></returns>
    public override async Task<PagedResult<TModel>> GetPagedResultAsync<TModel>(Expression<Func<TModel, bool>> predicate) where TModel : class => await _request.GetPagedResultAsync<TModel>(await GetQueryableAsync<TModel>(predicate));

    /// <summary>
    /// 写入一行数据到数据库
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="model">待写入的数据</param>
    /// <returns></returns>
    protected override async Task InternalAddAsync<TModel>(TModel model)
    {
        //await UniqueAsync(model);
        Create(model);
        Update(model);
        await (await GetOrCreateCollectionAsync<TModel>()).InsertOneAsync(model);
    }

    /// <summary>
    /// 写入多行数据到数据库
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="data">待写入的数据</param>
    /// <returns></returns>
    protected override async Task InternalAddsAsync<TModel>(IEnumerable<TModel> data)
    {
        foreach (var model in data)
        {
            //await UniqueAsync(model);
            Create(model);
            Update(model);
        }

        await (await GetOrCreateCollectionAsync<TModel>()).InsertManyAsync(data);
    }

    protected override async Task InternalUpdateAsync<TModel>(TModel model) => await (await GetOrCreateCollectionAsync<TModel>()).UpdateOneAsync(x => x.Id == model.Id, BsonDocument.Create(model));
    protected override async Task InternalUpdatesAsync<TModel>(IEnumerable<TModel> data) => await (await GetOrCreateCollectionAsync<TModel>()).UpdateManyAsync(x => data.Select(a => a.Id).Contains(x.Id), BsonDocument.Create(data));

    public override async Task DeleteAsync<TModel, TKey>(TKey id, DataServiceContext? context = null)
    {
        await (await GetOrCreateCollectionAsync<TModel>()).DeleteOneAsync(m => m.Id.Equals(id));
    }
    public override async Task DeletesAsync<TModel, TKey>(IEnumerable<TKey> ids, DataServiceContext? context = null)
    {
        //if (ids.IsNotNullOrEmpty())
        //{
        //    await (await GetOrCreateCollectionAsync<TModel>()).DeleteManyAsync(m => ids.Contains(m.Id));
        //}
        throw new NotImplementedException();
    }
    /// <summary>
    /// 获取或创建 MongoDB 的数据集合
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <returns></returns>
    private async Task<IMongoCollection<TModel>> GetOrCreateCollectionAsync<TModel>()
    {
        var tableName = typeof(TModel).GetTableName() ?? _endpoint.DataTable;
        var collection = _database.GetCollection<TModel>(tableName);
        if (collection == null)
        {
            await _database.CreateCollectionAsync(tableName);
            collection = _database.GetCollection<TModel>(tableName);
        }

        return collection;
    }

    public virtual void Create<Tmodel>(Tmodel model)
    {
        if (model is MongoModelWithCreate mongoModelWithCreate)
        {
            mongoModelWithCreate.Create();
        }

        //if (model is CipherMongoModel  cipherMongoModel)
        //{
        //    cipherMongoModel.SetHashCode();
        //}

        //if (_httpSetting.DataIsolate != DataIsolateLevel.None && model is IsolateMongoModelWithCreate dataIsolate)
        //{
        //    var dataIsolateId = _httpContext.GetClaimValue<string>(AppSettings.CLAIM_TYPE_DATA_ISOLATE_ID); ;
        //    if (dataIsolateId is not null)
        //    {
        //        dataIsolate.Isolate = dataIsolateId;
        //    }
        //}
    }

    private void Update<TModel>(TModel model)
    {
        if (model is MongoModelWithCreateAndUpdate modelWithCreateAndUpdate)
        {
            modelWithCreateAndUpdate.Update();
        }
    }
}